/*
 *  This file is part of Social Media Monitoring Toolbox.
 *
 *  Social Media Monitoring Toolbox is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Social Media Monitoring Toolbox is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Social Media Monitoring Toolbox. If not, see <http://www.gnu.org/licenses/lgpl.html>.
 *
 * � Copyright 2013
 * Jana Asmussen, Julian Bau�, Daniela Dalock, Christian Gutjahr,
 * Fabian Heidorn, Alexander Kaack, Vitali Kagadij 
 * 
 */

package data;

import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import types.SortDirection;
import types.YoutubeVideoType;

import types.AbstractPost;
import types.Author;
import types.FilterTyp;
import types.GeoTag;
import types.StreamDescriptor;
import types.TweetPost;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.google.code.morphia.query.Query;
import com.google.code.morphia.query.UpdateOperations;
import com.mongodb.Mongo;

public class Data implements IData,Serializable {
	
	private static final long serialVersionUID = -393596368121428817L;

	private static final String STREAMS_COLLECTION = "streams";

	private static Morphia morphia;
	private static Mongo mongo;

	public Data() {
		try {

			mongo = new Mongo();
			morphia = new Morphia();
			morphia.map(TweetPost.class).map(StreamDescriptor.class);
			morphia.map(TweetPost.class).map(Author.class);
			morphia.map(TweetPost.class).map(GeoTag.class);

		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
	}

	public List<StreamDescriptor> getStreams() {
		Datastore ds = morphia.createDatastore(mongo, STREAMS_COLLECTION);
		List<StreamDescriptor> tweets = ds.find(StreamDescriptor.class)
				.asList();

		return tweets;
	}

	public void addStream(StreamDescriptor streamDescriptor) {
		Datastore ds = morphia.createDatastore(mongo, STREAMS_COLLECTION);
		ds.save(streamDescriptor);
	}

	public void deleteStream(StreamDescriptor streamDescriptor) {
		// Delete Datastore
		Datastore ds = morphia.createDatastore(mongo, streamDescriptor
				.getObjectId().toString());
		ds.delete(ds.createQuery(streamDescriptor.getElementType()));

		// Delete Stream
		Datastore streams = morphia.createDatastore(mongo, STREAMS_COLLECTION);
		streams.delete(StreamDescriptor.class, streamDescriptor.getObjectId());
		streamDescriptor = null;

	}

	public void savePost(AbstractPost obj) {
		// Preconditions
		if (obj.getStreamDescriptor().getObjectId() == null) {
			throw new IllegalArgumentException(
					"StreamDescriptor has to be persisted in Database");
		}

		Datastore ds = morphia.createDatastore(mongo, obj.getStreamDescriptor()
				.getObjectId().toString());
		ds.save(obj);
	}

	public List<TweetPost> getTweetPosts(StreamDescriptor streamDescr,
			FilterTyp filter) {
		if (streamDescr.getObjectId() == null) {
			throw new IllegalArgumentException(
					"StreamDescriptor has to be persisted in Database");
		}
		Datastore ds = morphia.createDatastore(mongo, streamDescr.getObjectId()
				.toString());
		Query<TweetPost> q = ds.createQuery(TweetPost.class);
		if (filter != null) {
			if (filter.getLimitAuthorAge() != null) {
				// limit by author age
				filtergetLimitAuthorAge(q, "author.yearOfBirth", filter);
			}

			if (filter.getLimitAuthorName() != null) {
				// limit by author name
				filtergetLimitAuthorName(q, "author.name", filter);
			}

			if (filter.getLimitAuthorGender() != null) {
				// limit by author gender
				filtergetLimitAuthorGender(q, "author.gender", filter);
			}

			if (filter.getAuthor() != null) {
				// sort by author name
				sortByAuthor(q, "author.name", filter);
			}
			if (filter.getTime() != null) {
				// sort by time
				sortByTime(q, "dateOfCreation", filter);
			}
			if (filter.getNumberOfRetweets() != null) {
				// limit by number of retweets
				limitNumberOfRetweets(q, "retweetCount", filter);
			}
			if (filter.getLimitTime() != null) {
				// limit by timeslot
				limitTimeSlot(q, "dateOfCreation", filter);
			}
			if (filter.getSentiment() != null) {
				// limit by sentiment
				q.field("sentiment").equal(filter.getSentiment());
			}
		}

		return q.asList();
	}
	
	public List<YoutubeVideoType> getYouTubeVideos(
			StreamDescriptor streamDesciptor, FilterTyp filterTyp) {
		if (streamDesciptor.getObjectId() == null) {
			throw new IllegalArgumentException(
					"StreamDescriptor has to be persisted in Database");
		}
		Datastore ds = morphia.createDatastore(mongo, streamDesciptor
				.getObjectId().toString());
		
		Query<YoutubeVideoType> q = ds.createQuery(YoutubeVideoType.class);
		if (filterTyp != null) {
			if (filterTyp.getLimitAuthorAge() != null) {
				// limit by author age
				filtergetLimitAuthorAge(q, "author.yearOfBirth", filterTyp);
			}

			if (filterTyp.getLimitAuthorName() != null) {
				// limit by author name
				filtergetLimitAuthorName(q, "author.name", filterTyp);
			}

			if (filterTyp.getLimitAuthorGender() != null) {
				// limit by author gender
				filtergetLimitAuthorGender(q, "author.gender", filterTyp);
			}

			if (filterTyp.getAuthor() != null) {
				// sort by author name
				sortByAuthor(q, "author.name", filterTyp);
			}
			if (filterTyp.getTime() != null) {
				// sort by time				
				sortByTime(q, "dateOfCreation", filterTyp);
			}

			if (filterTyp.getLimitTime() != null) {
				// limit by timeslot
				limitTimeSlot(q, "dateOfCreation", filterTyp);
			}
			 if (filterTyp.getNumberOfResponses() != null) {
				 // limit by number of comments
					limitNumberOfComments(q, "commentCount", filterTyp);
			 }
		} 
		return q.asList();
	}


	private void limitNumberOfComments(Query<?> q,
			String key, FilterTyp filterTyp) {

		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}

		q.field(key).greaterThanOrEq(filterTyp.getNumberOfResponses().getBegin());
		q.field(key).lessThanOrEq(filterTyp.getNumberOfResponses().getEnd());
	}

	private void limitTimeSlot(Query<?> q, String key, FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}
		Date start = filter.getLimitTime().getBeginDate();
		Date end = filter.getLimitTime().getEndDate();

		q.field(key).greaterThanOrEq(start);
		q.field(key).lessThanOrEq(end);
	}

	private void limitNumberOfRetweets(Query<?> q, String key,
			FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}

		q.field(key).greaterThanOrEq(filter.getNumberOfRetweets().getBegin());
		q.field(key).lessThanOrEq(filter.getNumberOfRetweets().getEnd());
	}

	private void sortByTime(Query<?> q, String key, FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}
		if (filter.getTime().equals(SortDirection.asc)) {
			q.order(key);
		}
		if (filter.getTime().equals(SortDirection.desc)) {
			q.order("-" + key);
		}
	}

	private void sortByAuthor(Query<?> q, String key, FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}
		if (filter.getAuthor().equals(SortDirection.asc)) {
			q.order(key);
		}
		if (filter.getAuthor().equals(SortDirection.desc)) {
			q.order("-" + key);
		}
	}

	private void filtergetLimitAuthorGender(Query<?> q, String key,
			FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}
		q.field(key).equal(filter.getLimitAuthorGender());
	}

	private void filtergetLimitAuthorName(Query<?> q, String key,
			FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}
		q.field(key).equal(filter.getLimitAuthorName());
	}

	private void filtergetLimitAuthorAge(Query<?> q,
			String key, FilterTyp filter) {
		if (key == null) {
			throw new IllegalArgumentException("empty String");
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date()); // heute
		int currentYear = cal.get(Calendar.YEAR);
		int beginn = currentYear - filter.getLimitAuthorAge().getBegin();
		int end = currentYear - filter.getLimitAuthorAge().getEnd();
		q.field(key).greaterThanOrEq(end);
		q.field(key).lessThanOrEq(beginn);
//		return q;
	}


	public void updatePost(AbstractPost post) {
		Datastore ds = morphia.createDatastore(mongo, post
				.getStreamDescriptor().getObjectId().toString());
		ds.save(post);

	}

	public void deletePost(AbstractPost post) {
		Datastore ds = morphia.createDatastore(mongo, post
				.getStreamDescriptor().getObjectId().toString());
		ds.delete(post.getStreamDescriptor().getElementType(), post.getId());

	}
}
